--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
meshchatx/src/backend/map_manager.py 93974edf3f77e2de3301e3e9295a7bece80d89cc (93974edf) Text, 12.69 KB
Tff7b72import T7ee787base64
Tff7b72import T7ee787concurrentT7ee787.T7ee787futures
Tff7b72import T7ee787math
Tff7b72import T7ee787os
Tff7b72import T7ee787sqlite3
Tff7b72import T7ee787threading
Tff7b72import T7ee787time
Tff7b72import T7ee787requests
Tff7b72import T7ee787RNS
T8b949e# 1x1 transparent PNG to return when a tile is not found in offline mode
Te6edf3TRANSPARENT_TILE Tff7b72= Te6edf3base64Tff7b72.Td2a8ffb64decodeTb4b4b4(
Ta5d6ff"Ta5d6ffiVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=Ta5d6ff"Tb4b4b4,
Tb4b4b4)
Tff7b72class T56d364MapManagerTb4b4b4:
Tff7b72def Tff7b72__init__Tb4b4b4(Tff7b72selfTb4b4b4, Te6edf3config_managerTb4b4b4, Te6edf3storage_dirTb4b4b4)Tb4b4b4:
Tff7b72selfTff7b72.Td2a8ffconfig Tff7b72= Te6edf3config_manager
Tff7b72selfTff7b72.Td2a8ffstorage_dir Tff7b72= Te6edf3storage_dir
Tff7b72selfTff7b72.Td2a8ff_local Tff7b72= Te6edf3threadingTff7b72.Td2a8fflocalTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ff_metadata_cache Tff7b72= Tff7b72None
Tff7b72selfTff7b72.Td2a8ff_export_progress Tff7b72= Tb4b4b4{Tb4b4b4}
Tff7b72selfTff7b72.Td2a8ff_export_cancelled Tff7b72= Tffa657setTb4b4b4(Tb4b4b4)
Tff7b72def Td2a8ffget_connectionTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3pathTb4b4b4)Tb4b4b4:
Tff7b72if Tff7b72not Tffa657hasattrTb4b4b4(Tff7b72selfTff7b72.Td2a8ff_localTb4b4b4, Ta5d6ff"Ta5d6ffconnectionsTa5d6ff"Tb4b4b4)Tb4b4b4:
Tff7b72selfTff7b72.Td2a8ff_localTff7b72.Td2a8ffconnections Tff7b72= Tb4b4b4{Tb4b4b4}
Tff7b72if Te6edf3path Tff7b72not Tff7b72in Tff7b72selfTff7b72.Td2a8ff_localTff7b72.Td2a8ffconnectionsTb4b4b4:
Tff7b72if Tff7b72not Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffexistsTb4b4b4(Te6edf3pathTb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72None
Te6edf3conn Tff7b72= Te6edf3sqlite3Tff7b72.Td2a8ffconnectTb4b4b4(Te6edf3pathTb4b4b4, Te6edf3check_same_threadTff7b72=Tff7b72FalseTb4b4b4)
Te6edf3connTff7b72.Td2a8ffrow_factory Tff7b72= Te6edf3sqlite3Tff7b72.Td2a8ffRow
Tff7b72selfTff7b72.Td2a8ff_localTff7b72.Td2a8ffconnectionsTb4b4b4[Te6edf3pathTb4b4b4] Tff7b72= Te6edf3conn
Tff7b72return Tff7b72selfTff7b72.Td2a8ff_localTff7b72.Td2a8ffconnectionsTb4b4b4[Te6edf3pathTb4b4b4]
Tff7b72def Td2a8ffget_offline_pathTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Te6edf3path Tff7b72= Tff7b72selfTff7b72.Td2a8ffconfigTff7b72.Td2a8ffmap_offline_pathTff7b72.Td2a8ffgetTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3pathTb4b4b4:
Tff7b72return Te6edf3path
T8b949e# Fallback to default if not set but file exists
Te6edf3default_path Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Tff7b72selfTff7b72.Td2a8ffstorage_dirTb4b4b4, Ta5d6ff"Ta5d6ffoffline_map.mbtilesTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffexistsTb4b4b4(Te6edf3default_pathTb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3default_path
Tff7b72return Tff7b72None
Tff7b72def Td2a8ffget_mbtiles_dirTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Te6edf3dir_path Tff7b72= Tff7b72selfTff7b72.Td2a8ffconfigTff7b72.Td2a8ffmap_mbtiles_dirTff7b72.Td2a8ffgetTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3dir_path Tff7b72and Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffisdirTb4b4b4(Te6edf3dir_pathTb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3dir_path
Tff7b72return Tff7b72selfTff7b72.Td2a8ffstorage_dir
Tff7b72def Td2a8fflist_mbtilesTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Te6edf3mbtiles_dir Tff7b72= Tff7b72selfTff7b72.Td2a8ffget_mbtiles_dirTb4b4b4(Tb4b4b4)
Te6edf3files Tff7b72= Tb4b4b4[Tb4b4b4]
Tff7b72if Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffexistsTb4b4b4(Te6edf3mbtiles_dirTb4b4b4)Tb4b4b4:
Tff7b72for Te6edf3f Tff7b72in Te6edf3osTff7b72.Td2a8fflistdirTb4b4b4(Te6edf3mbtiles_dirTb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3fTff7b72.Td2a8ffendswithTb4b4b4(Ta5d6ff"Ta5d6ff.mbtilesTa5d6ff"Tb4b4b4)Tb4b4b4:
Te6edf3full_path Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3mbtiles_dirTb4b4b4, Te6edf3fTb4b4b4)
Te6edf3stats Tff7b72= Te6edf3osTff7b72.Td2a8ffstatTb4b4b4(Te6edf3full_pathTb4b4b4)
Te6edf3filesTff7b72.Td2a8ffappendTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffnameTa5d6ff"Tb4b4b4: Te6edf3fTb4b4b4,
Ta5d6ff"Ta5d6ffpathTa5d6ff"Tb4b4b4: Te6edf3full_pathTb4b4b4,
Ta5d6ff"Ta5d6ffsizeTa5d6ff"Tb4b4b4: Te6edf3statsTff7b72.Td2a8ffst_sizeTb4b4b4,
Ta5d6ff"Ta5d6ffmtimeTa5d6ff"Tb4b4b4: Te6edf3statsTff7b72.Td2a8ffst_mtimeTb4b4b4,
Ta5d6ff"Ta5d6ffis_activeTa5d6ff"Tb4b4b4: Te6edf3full_path Tff7b72== Tff7b72selfTff7b72.Td2a8ffget_offline_pathTb4b4b4(Tb4b4b4)Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tff7b72return Tffa657sortedTb4b4b4(Te6edf3filesTb4b4b4, Te6edf3keyTff7b72=Tff7b72lambda Te6edf3xTb4b4b4: Te6edf3xTb4b4b4[Ta5d6ff"Ta5d6ffmtimeTa5d6ff"Tb4b4b4]Tb4b4b4, Te6edf3reverseTff7b72=Tff7b72TrueTb4b4b4)
Tff7b72def Td2a8ffdelete_mbtilesTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3filenameTb4b4b4)Tb4b4b4:
Te6edf3mbtiles_dir Tff7b72= Tff7b72selfTff7b72.Td2a8ffget_mbtiles_dirTb4b4b4(Tb4b4b4)
Te6edf3file_path Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3mbtiles_dirTb4b4b4, Te6edf3filenameTb4b4b4)
Tff7b72if Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffexistsTb4b4b4(Te6edf3file_pathTb4b4b4) Tff7b72and Te6edf3file_pathTff7b72.Td2a8ffendswithTb4b4b4(Ta5d6ff"Ta5d6ff.mbtilesTa5d6ff"Tb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3file_path Tff7b72== Tff7b72selfTff7b72.Td2a8ffget_offline_pathTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72selfTff7b72.Td2a8ffconfigTff7b72.Td2a8ffmap_offline_pathTff7b72.Td2a8ffsetTb4b4b4(Tff7b72NoneTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffconfigTff7b72.Td2a8ffmap_offline_enabledTff7b72.Td2a8ffsetTb4b4b4(Tff7b72FalseTb4b4b4)
Te6edf3osTff7b72.Td2a8ffremoveTb4b4b4(Te6edf3file_pathTb4b4b4)
Tff7b72selfTff7b72.Td2a8ff_metadata_cache Tff7b72= Tff7b72None
Tff7b72return Tff7b72True
Tff7b72return Tff7b72False
Tff7b72def Td2a8ffget_metadataTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Te6edf3path Tff7b72= Tff7b72selfTff7b72.Td2a8ffget_offline_pathTb4b4b4(Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3path Tff7b72or Tff7b72not Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffexistsTb4b4b4(Te6edf3pathTb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72None
Tff7b72if Tff7b72selfTff7b72.Td2a8ff_metadata_cache Tff7b72and Tff7b72selfTff7b72.Td2a8ff_metadata_cacheTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffpathTa5d6ff"Tb4b4b4) Tff7b72== Te6edf3pathTb4b4b4:
Tff7b72return Tff7b72selfTff7b72.Td2a8ff_metadata_cache
Te6edf3conn Tff7b72= Tff7b72selfTff7b72.Td2a8ffget_connectionTb4b4b4(Te6edf3pathTb4b4b4)
Tff7b72if Tff7b72not Te6edf3connTb4b4b4:
Tff7b72return Tff7b72None
Tff7b72tryTb4b4b4:
Te6edf3cursor Tff7b72= Te6edf3connTff7b72.Td2a8ffcursorTb4b4b4(Tb4b4b4)
Te6edf3cursorTff7b72.Td2a8ffexecuteTb4b4b4(Ta5d6ff"Ta5d6ffSELECT name, value FROM metadataTa5d6ff"Tb4b4b4)
Te6edf3rows Tff7b72= Te6edf3cursorTff7b72.Td2a8fffetchallTb4b4b4(Tb4b4b4)
Te6edf3metadata Tff7b72= Tb4b4b4{Te6edf3rowTb4b4b4[Ta5d6ff"Ta5d6ffnameTa5d6ff"Tb4b4b4]Tb4b4b4: Te6edf3rowTb4b4b4[Ta5d6ff"Ta5d6ffvalueTa5d6ff"Tb4b4b4] Tff7b72for Te6edf3row Tff7b72in Te6edf3rowsTb4b4b4}
Te6edf3metadataTb4b4b4[Ta5d6ff"Ta5d6ffpathTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3path
T8b949e# Basic validation: ensure it's raster (format is not pbf)
Tff7b72if Te6edf3metadataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffformatTa5d6ff"Tb4b4b4) Tff7b72== Ta5d6ff"Ta5d6ffpbfTa5d6ff"Tb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(
Ta5d6ff"Ta5d6ffMBTiles file is in vector (PBF) format, which is not supported.Ta5d6ff"Tb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffLOG_ERRORTb4b4b4,
Tb4b4b4)
Tff7b72return Tff7b72None
Tff7b72selfTff7b72.Td2a8ff_metadata_cache Tff7b72= Te6edf3metadata
Tff7b72return Te6edf3metadata
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffError reading MBTiles metadata: Tffd700{Te6edf3eTffd700}Ta5d6ff"Tb4b4b4, Te6edf3RNSTff7b72.Td2a8ffLOG_ERRORTb4b4b4)
Tff7b72return Tff7b72None
Tff7b72def Td2a8ffget_tileTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3zTb4b4b4, Te6edf3xTb4b4b4, Te6edf3yTb4b4b4)Tb4b4b4:
Te6edf3path Tff7b72= Tff7b72selfTff7b72.Td2a8ffget_offline_pathTb4b4b4(Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3path Tff7b72or Tff7b72not Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffexistsTb4b4b4(Te6edf3pathTb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72None
Te6edf3conn Tff7b72= Tff7b72selfTff7b72.Td2a8ffget_connectionTb4b4b4(Te6edf3pathTb4b4b4)
Tff7b72if Tff7b72not Te6edf3connTb4b4b4:
Tff7b72return Tff7b72None
Tff7b72tryTb4b4b4:
T8b949e# MBTiles uses TMS tiling scheme (y is flipped)
Te6edf3tms_y Tff7b72= Tb4b4b4(T79c0ff1 Tff7b72<< Te6edf3zTb4b4b4) Tff7b72- T79c0ff1 Tff7b72- Te6edf3y
Te6edf3cursor Tff7b72= Te6edf3connTff7b72.Td2a8ffcursorTb4b4b4(Tb4b4b4)
Te6edf3cursorTff7b72.Td2a8ffexecuteTb4b4b4(
Ta5d6ff"Ta5d6ffSELECT tile_data FROM tiles WHERE zoom_level = ? AND tile_column = ? AND tile_row = ?Ta5d6ff"Tb4b4b4,
Tb4b4b4(Te6edf3zTb4b4b4, Te6edf3xTb4b4b4, Te6edf3tms_yTb4b4b4)Tb4b4b4,
Tb4b4b4)
Te6edf3row Tff7b72= Te6edf3cursorTff7b72.Td2a8fffetchoneTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3rowTb4b4b4:
Tff7b72return Te6edf3rowTb4b4b4[Ta5d6ff"Ta5d6fftile_dataTa5d6ff"Tb4b4b4]
Tff7b72return Tff7b72None
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffError reading MBTiles tile Tffd700{Te6edf3zTffd700}Ta5d6ff/Tffd700{Te6edf3xTffd700}Ta5d6ff/Tffd700{Te6edf3yTffd700}Ta5d6ff: Tffd700{Te6edf3eTffd700}Ta5d6ff"Tb4b4b4, Te6edf3RNSTff7b72.Td2a8ffLOG_ERRORTb4b4b4)
Tff7b72return Tff7b72None
Tff7b72def Td2a8ffstart_exportTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3export_idTb4b4b4, Te6edf3bboxTb4b4b4, Te6edf3min_zoomTb4b4b4, Te6edf3max_zoomTb4b4b4, Te6edf3nameTff7b72=Ta5d6ff"Ta5d6ffExported MapTa5d6ff"Tb4b4b4)Tb4b4b4:
T8b949e"""Start downloading tiles and creating an MBTiles file in a background thread."""
Te6edf3thread Tff7b72= Te6edf3threadingTff7b72.Td2a8ffThreadTb4b4b4(
Te6edf3targetTff7b72=Tff7b72selfTff7b72.Td2a8ff_run_exportTb4b4b4,
Te6edf3argsTff7b72=Tb4b4b4(Te6edf3export_idTb4b4b4, Te6edf3bboxTb4b4b4, Te6edf3min_zoomTb4b4b4, Te6edf3max_zoomTb4b4b4, Te6edf3nameTb4b4b4)Tb4b4b4,
Te6edf3daemonTff7b72=Tff7b72TrueTb4b4b4,
Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ff_export_progressTb4b4b4[Te6edf3export_idTb4b4b4] Tff7b72= Tb4b4b4{
Ta5d6ff"Ta5d6ffstatusTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffstartingTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffprogressTa5d6ff"Tb4b4b4: T79c0ff0Tb4b4b4,
Ta5d6ff"Ta5d6fftotalTa5d6ff"Tb4b4b4: T79c0ff0Tb4b4b4,
Ta5d6ff"Ta5d6ffcurrentTa5d6ff"Tb4b4b4: T79c0ff0Tb4b4b4,
Ta5d6ff"Ta5d6ffstart_timeTa5d6ff"Tb4b4b4: Te6edf3timeTff7b72.Td2a8fftimeTb4b4b4(Tb4b4b4)Tb4b4b4,
Tb4b4b4}
Te6edf3threadTff7b72.Td2a8ffstartTb4b4b4(Tb4b4b4)
Tff7b72return Te6edf3export_id
Tff7b72def Td2a8ffget_export_statusTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3export_idTb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72selfTff7b72.Td2a8ff_export_progressTff7b72.Td2a8ffgetTb4b4b4(Te6edf3export_idTb4b4b4)
Tff7b72def Td2a8ffcancel_exportTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3export_idTb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3export_id Tff7b72in Tff7b72selfTff7b72.Td2a8ff_export_progressTb4b4b4:
Tff7b72selfTff7b72.Td2a8ff_export_cancelledTff7b72.Td2a8ffaddTb4b4b4(Te6edf3export_idTb4b4b4)
T8b949e# If it's already failed or completed, just clean up
Te6edf3status Tff7b72= Tff7b72selfTff7b72.Td2a8ff_export_progressTb4b4b4[Te6edf3export_idTb4b4b4]Tff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffstatusTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3status Tff7b72in Tb4b4b4[Ta5d6ff"Ta5d6ffcompletedTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6fffailedTa5d6ff"Tb4b4b4]Tb4b4b4:
Te6edf3file_path Tff7b72= Tff7b72selfTff7b72.Td2a8ff_export_progressTb4b4b4[Te6edf3export_idTb4b4b4]Tff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fffile_pathTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3file_path Tff7b72and Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffexistsTb4b4b4(Te6edf3file_pathTb4b4b4)Tb4b4b4:
Te6edf3osTff7b72.Td2a8ffremoveTb4b4b4(Te6edf3file_pathTb4b4b4)
Tff7b72del Tff7b72selfTff7b72.Td2a8ff_export_progressTb4b4b4[Te6edf3export_idTb4b4b4]
Tff7b72if Te6edf3export_id Tff7b72in Tff7b72selfTff7b72.Td2a8ff_export_cancelledTb4b4b4:
Tff7b72selfTff7b72.Td2a8ff_export_cancelledTff7b72.Td2a8ffremoveTb4b4b4(Te6edf3export_idTb4b4b4)
Tff7b72return Tff7b72True
Tff7b72return Tff7b72False
Tff7b72def Td2a8ff_run_exportTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3export_idTb4b4b4, Te6edf3bboxTb4b4b4, Te6edf3min_zoomTb4b4b4, Te6edf3max_zoomTb4b4b4, Te6edf3nameTb4b4b4)Tb4b4b4:
T8b949e# bbox: [min_lon, min_lat, max_lon, max_lat]
Te6edf3min_lonTb4b4b4, Te6edf3min_latTb4b4b4, Te6edf3max_lonTb4b4b4, Te6edf3max_lat Tff7b72= Te6edf3bbox
T8b949e# collect all tiles to download
Te6edf3tiles_to_download Tff7b72= Tb4b4b4[Tb4b4b4]
Te6edf3zoom_levels Tff7b72= Tffa657rangeTb4b4b4(Te6edf3min_zoomTb4b4b4, Te6edf3max_zoom Tff7b72+ T79c0ff1Tb4b4b4)
Tff7b72for Te6edf3z Tff7b72in Te6edf3zoom_levelsTb4b4b4:
Te6edf3x1Tb4b4b4, Te6edf3y1 Tff7b72= Tff7b72selfTff7b72.Td2a8ff_lonlat_to_tileTb4b4b4(Te6edf3min_lonTb4b4b4, Te6edf3max_latTb4b4b4, Te6edf3zTb4b4b4)
Te6edf3x2Tb4b4b4, Te6edf3y2 Tff7b72= Tff7b72selfTff7b72.Td2a8ff_lonlat_to_tileTb4b4b4(Te6edf3max_lonTb4b4b4, Te6edf3min_latTb4b4b4, Te6edf3zTb4b4b4)
Te6edf3tiles_to_downloadTff7b72.Td2a8ffextendTb4b4b4(
Tb4b4b4(Te6edf3zTb4b4b4, Te6edf3xTb4b4b4, Te6edf3yTb4b4b4) Tff7b72for Te6edf3x Tff7b72in Tffa657rangeTb4b4b4(Te6edf3x1Tb4b4b4, Te6edf3x2 Tff7b72+ T79c0ff1Tb4b4b4) Tff7b72for Te6edf3y Tff7b72in Tffa657rangeTb4b4b4(Te6edf3y1Tb4b4b4, Te6edf3y2 Tff7b72+ T79c0ff1Tb4b4b4)
Tb4b4b4)
Te6edf3total_tiles Tff7b72= Tffa657lenTb4b4b4(Te6edf3tiles_to_downloadTb4b4b4)
Tff7b72selfTff7b72.Td2a8ff_export_progressTb4b4b4[Te6edf3export_idTb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6fftotalTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3total_tiles
Tff7b72selfTff7b72.Td2a8ff_export_progressTb4b4b4[Te6edf3export_idTb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6ffstatusTa5d6ff"Tb4b4b4] Tff7b72= Ta5d6ff"Ta5d6ffdownloadingTa5d6ff"
Te6edf3dest_path Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Tff7b72selfTff7b72.Td2a8ffstorage_dirTb4b4b4, Ta5d6fffTa5d6ff"Ta5d6ffexport_Tffd700{Te6edf3export_idTffd700}Ta5d6ff.mbtilesTa5d6ff"Tb4b4b4)
Tff7b72tryTb4b4b4:
Te6edf3conn Tff7b72= Te6edf3sqlite3Tff7b72.Td2a8ffconnectTb4b4b4(Te6edf3dest_pathTb4b4b4)
Te6edf3cursor Tff7b72= Te6edf3connTff7b72.Td2a8ffcursorTb4b4b4(Tb4b4b4)
T8b949e# create schema
Te6edf3cursorTff7b72.Td2a8ffexecuteTb4b4b4(Ta5d6ff"Ta5d6ffCREATE TABLE metadata (name text, value text)Ta5d6ff"Tb4b4b4)
Te6edf3cursorTff7b72.Td2a8ffexecuteTb4b4b4(
Ta5d6ff"Ta5d6ffCREATE TABLE tiles (zoom_level integer, tile_column integer, tile_row integer, tile_data blob)Ta5d6ff"Tb4b4b4,
Tb4b4b4)
Te6edf3cursorTff7b72.Td2a8ffexecuteTb4b4b4(
Ta5d6ff"Ta5d6ffCREATE UNIQUE INDEX tile_index on tiles (zoom_level, tile_column, tile_row)Ta5d6ff"Tb4b4b4,
Tb4b4b4)
T8b949e# insert metadata
Te6edf3metadata Tff7b72= Tb4b4b4[
Tb4b4b4(Ta5d6ff"Ta5d6ffnameTa5d6ff"Tb4b4b4, Te6edf3nameTb4b4b4)Tb4b4b4,
Tb4b4b4(Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffbaselayerTa5d6ff"Tb4b4b4)Tb4b4b4,
Tb4b4b4(Ta5d6ff"Ta5d6ffversionTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff1.1Ta5d6ff"Tb4b4b4)Tb4b4b4,
Tb4b4b4(Ta5d6ff"Ta5d6ffdescriptionTa5d6ff"Tb4b4b4, Ta5d6fffTa5d6ff"Ta5d6ffExported from MeshChatX on Tffd700{Te6edf3timeTff7b72.Td2a8ffctimeTb4b4b4(Tb4b4b4)Tffd700}Ta5d6ff"Tb4b4b4)Tb4b4b4,
Tb4b4b4(Ta5d6ff"Ta5d6ffformatTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffpngTa5d6ff"Tb4b4b4)Tb4b4b4,
Tb4b4b4(Ta5d6ff"Ta5d6ffboundsTa5d6ff"Tb4b4b4, Ta5d6fffTa5d6ff"Tffd700{Te6edf3min_lonTffd700}Ta5d6ff,Tffd700{Te6edf3min_latTffd700}Ta5d6ff,Tffd700{Te6edf3max_lonTffd700}Ta5d6ff,Tffd700{Te6edf3max_latTffd700}Ta5d6ff"Tb4b4b4)Tb4b4b4,
Tb4b4b4]
Te6edf3cursorTff7b72.Td2a8ffexecutemanyTb4b4b4(Ta5d6ff"Ta5d6ffINSERT INTO metadata VALUES (?, ?)Ta5d6ff"Tb4b4b4, Te6edf3metadataTb4b4b4)
Te6edf3connTff7b72.Td2a8ffcommitTb4b4b4(Tb4b4b4)
Te6edf3tile_server_url Tff7b72= Tff7b72selfTff7b72.Td2a8ffconfigTff7b72.Td2a8ffmap_tile_server_urlTff7b72.Td2a8ffgetTb4b4b4(Tb4b4b4)
Te6edf3current_count Tff7b72= T79c0ff0
T8b949e# download tiles in parallel
T8b949e# using 10 workers for a good balance between speed and being polite
Te6edf3max_workers Tff7b72= T79c0ff10
Tff7b72def Td2a8ffdownload_tileTb4b4b4(Te6edf3tile_coordsTb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3export_id Tff7b72in Tff7b72selfTff7b72.Td2a8ff_export_cancelledTb4b4b4:
Tff7b72return Tff7b72None
Te6edf3zTb4b4b4, Te6edf3xTb4b4b4, Te6edf3y Tff7b72= Te6edf3tile_coords
Te6edf3tile_url Tff7b72= Tb4b4b4(
Te6edf3tile_server_urlTff7b72.Td2a8ffreplaceTb4b4b4(Ta5d6ff"Tffd700{z}Ta5d6ff"Tb4b4b4, Tffa657strTb4b4b4(Te6edf3zTb4b4b4)Tb4b4b4)
Tff7b72.Td2a8ffreplaceTb4b4b4(Ta5d6ff"Tffd700{x}Ta5d6ff"Tb4b4b4, Tffa657strTb4b4b4(Te6edf3xTb4b4b4)Tb4b4b4)
Tff7b72.Td2a8ffreplaceTb4b4b4(Ta5d6ff"Tffd700{y}Ta5d6ff"Tb4b4b4, Tffa657strTb4b4b4(Te6edf3yTb4b4b4)Tb4b4b4)
Tb4b4b4)
Tff7b72tryTb4b4b4:
T8b949e# small per-thread delay to avoid overwhelming servers
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff0.02Tb4b4b4)
Te6edf3response Tff7b72= Te6edf3requestsTff7b72.Td2a8ffgetTb4b4b4(
Te6edf3tile_urlTb4b4b4,
Te6edf3headersTff7b72=Tb4b4b4{Ta5d6ff"Ta5d6ffUser-AgentTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffMeshChatX/1.0 MapExporterTa5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3timeoutTff7b72=T79c0ff15Tb4b4b4,
Tb4b4b4)
Tff7b72if Te6edf3responseTff7b72.Td2a8ffstatus_code Tff7b72== T79c0ff200Tb4b4b4:
T8b949e# MBTiles uses TMS (y flipped)
Te6edf3tms_y Tff7b72= Tb4b4b4(T79c0ff1 Tff7b72<< Te6edf3zTb4b4b4) Tff7b72- T79c0ff1 Tff7b72- Te6edf3y
Tff7b72return Tb4b4b4(Te6edf3zTb4b4b4, Te6edf3xTb4b4b4, Te6edf3tms_yTb4b4b4, Te6edf3responseTff7b72.Td2a8ffcontentTb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(
Ta5d6fffTa5d6ff"Ta5d6ffExport failed to download tile Tffd700{Te6edf3zTffd700}Ta5d6ff/Tffd700{Te6edf3xTffd700}Ta5d6ff/Tffd700{Te6edf3yTffd700}Ta5d6ff: Tffd700{Te6edf3eTffd700}Ta5d6ff"Tb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffLOG_ERRORTb4b4b4,
Tb4b4b4)
Tff7b72return Tff7b72None
Tff7b72with Te6edf3concurrentTff7b72.Td2a8fffuturesTff7b72.Td2a8ffThreadPoolExecutorTb4b4b4(
Te6edf3max_workersTff7b72=Te6edf3max_workersTb4b4b4,
Tb4b4b4) Tff7b72as Te6edf3executorTb4b4b4:
Te6edf3future_to_tile Tff7b72= Tb4b4b4{
Te6edf3executorTff7b72.Td2a8ffsubmitTb4b4b4(Te6edf3download_tileTb4b4b4, Te6edf3tileTb4b4b4)Tb4b4b4: Te6edf3tile
Tff7b72for Te6edf3tile Tff7b72in Te6edf3tiles_to_download
Tb4b4b4}
Te6edf3batch_size Tff7b72= T79c0ff50
Te6edf3batch_data Tff7b72= Tb4b4b4[Tb4b4b4]
Tff7b72for Te6edf3future Tff7b72in Te6edf3concurrentTff7b72.Td2a8fffuturesTff7b72.Td2a8ffas_completedTb4b4b4(Te6edf3future_to_tileTb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3export_id Tff7b72in Tff7b72selfTff7b72.Td2a8ff_export_cancelledTb4b4b4:
Te6edf3executorTff7b72.Td2a8ffshutdownTb4b4b4(Te6edf3waitTff7b72=Tff7b72FalseTb4b4b4, Te6edf3cancel_futuresTff7b72=Tff7b72TrueTb4b4b4)
Tff7b72break
Te6edf3result Tff7b72= Te6edf3futureTff7b72.Td2a8ffresultTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3resultTb4b4b4:
Te6edf3batch_dataTff7b72.Td2a8ffappendTb4b4b4(Te6edf3resultTb4b4b4)
Te6edf3current_count Tff7b72+Tff7b72= T79c0ff1
T8b949e# Update progress every few tiles or when batch is ready
Tff7b72if Te6edf3current_count Tff7b72% T79c0ff5 Tff7b72== T79c0ff0 Tff7b72or Te6edf3current_count Tff7b72== Te6edf3total_tilesTb4b4b4:
Tff7b72selfTff7b72.Td2a8ff_export_progressTb4b4b4[Te6edf3export_idTb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6ffcurrentTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3current_count
Tff7b72selfTff7b72.Td2a8ff_export_progressTb4b4b4[Te6edf3export_idTb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6ffprogressTa5d6ff"Tb4b4b4] Tff7b72= Tffa657intTb4b4b4(
Tb4b4b4(Te6edf3current_count Tff7b72/ Te6edf3total_tilesTb4b4b4) Tff7b72* T79c0ff100Tb4b4b4,
Tb4b4b4)
T8b949e# Write batches to database
Tff7b72if Tffa657lenTb4b4b4(Te6edf3batch_dataTb4b4b4) Tff7b72>Tff7b72= Te6edf3batch_size Tff7b72or Tb4b4b4(
Te6edf3current_count Tff7b72== Te6edf3total_tiles Tff7b72and Te6edf3batch_data
Tb4b4b4)Tb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3cursorTff7b72.Td2a8ffexecutemanyTb4b4b4(
Ta5d6ff"Ta5d6ffINSERT INTO tiles VALUES (?, ?, ?, ?)Ta5d6ff"Tb4b4b4,
Te6edf3batch_dataTb4b4b4,
Tb4b4b4)
Te6edf3connTff7b72.Td2a8ffcommitTb4b4b4(Tb4b4b4)
Te6edf3batch_data Tff7b72= Tb4b4b4[Tb4b4b4]
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffFailed to insert map tiles: Tffd700{Te6edf3eTffd700}Ta5d6ff"Tb4b4b4, Te6edf3RNSTff7b72.Td2a8ffLOG_ERRORTb4b4b4)
Tff7b72if Te6edf3export_id Tff7b72in Tff7b72selfTff7b72.Td2a8ff_export_cancelledTb4b4b4:
Te6edf3connTff7b72.Td2a8ffcloseTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffexistsTb4b4b4(Te6edf3dest_pathTb4b4b4)Tb4b4b4:
Te6edf3osTff7b72.Td2a8ffremoveTb4b4b4(Te6edf3dest_pathTb4b4b4)
Tff7b72if Te6edf3export_id Tff7b72in Tff7b72selfTff7b72.Td2a8ff_export_progressTb4b4b4:
Tff7b72del Tff7b72selfTff7b72.Td2a8ff_export_progressTb4b4b4[Te6edf3export_idTb4b4b4]
Tff7b72selfTff7b72.Td2a8ff_export_cancelledTff7b72.Td2a8ffremoveTb4b4b4(Te6edf3export_idTb4b4b4)
Tff7b72return
Te6edf3connTff7b72.Td2a8ffcloseTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ff_export_progressTb4b4b4[Te6edf3export_idTb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6ffstatusTa5d6ff"Tb4b4b4] Tff7b72= Ta5d6ff"Ta5d6ffcompletedTa5d6ff"
Tff7b72selfTff7b72.Td2a8ff_export_progressTb4b4b4[Te6edf3export_idTb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6fffile_pathTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3dest_path
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffMap export failed: Tffd700{Te6edf3eTffd700}Ta5d6ff"Tb4b4b4, Te6edf3RNSTff7b72.Td2a8ffLOG_ERRORTb4b4b4)
Tff7b72selfTff7b72.Td2a8ff_export_progressTb4b4b4[Te6edf3export_idTb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6ffstatusTa5d6ff"Tb4b4b4] Tff7b72= Ta5d6ff"Ta5d6fffailedTa5d6ff"
Tff7b72selfTff7b72.Td2a8ff_export_progressTb4b4b4[Te6edf3export_idTb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6fferrorTa5d6ff"Tb4b4b4] Tff7b72= Tffa657strTb4b4b4(Te6edf3eTb4b4b4)
Tff7b72if Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffexistsTb4b4b4(Te6edf3dest_pathTb4b4b4)Tb4b4b4:
Te6edf3osTff7b72.Td2a8ffremoveTb4b4b4(Te6edf3dest_pathTb4b4b4)
Tff7b72def Td2a8ff_lonlat_to_tileTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3lonTb4b4b4, Te6edf3latTb4b4b4, Te6edf3zoomTb4b4b4)Tb4b4b4:
Te6edf3lat_rad Tff7b72= Te6edf3mathTff7b72.Td2a8ffradiansTb4b4b4(Te6edf3latTb4b4b4)
Te6edf3n Tff7b72= T79c0ff2.0Tff7b72*Tff7b72*Te6edf3zoom
Te6edf3x Tff7b72= Tffa657intTb4b4b4(Tb4b4b4(Te6edf3lon Tff7b72+ T79c0ff180.0Tb4b4b4) Tff7b72/ T79c0ff360.0 Tff7b72* Te6edf3nTb4b4b4)
Te6edf3y Tff7b72= Tffa657intTb4b4b4(
Tb4b4b4(T79c0ff1.0 Tff7b72- Te6edf3mathTff7b72.Td2a8fflogTb4b4b4(Te6edf3mathTff7b72.Td2a8fftanTb4b4b4(Te6edf3lat_radTb4b4b4) Tff7b72+ Tb4b4b4(T79c0ff1 Tff7b72/ Te6edf3mathTff7b72.Td2a8ffcosTb4b4b4(Te6edf3lat_radTb4b4b4)Tb4b4b4)Tb4b4b4) Tff7b72/ Te6edf3mathTff7b72.Td2a8ffpiTb4b4b4)
Tff7b72/ T79c0ff2.0
Tff7b72* Te6edf3nTb4b4b4,
Tb4b4b4)
Tff7b72return Te6edf3xTb4b4b4, Te6edf3y
Tff7b72def Td2a8ffcloseTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Tff7b72if Tffa657hasattrTb4b4b4(Tff7b72selfTff7b72.Td2a8ff_localTb4b4b4, Ta5d6ff"Ta5d6ffconnectionsTa5d6ff"Tb4b4b4)Tb4b4b4:
Tff7b72for Te6edf3conn Tff7b72in Tff7b72selfTff7b72.Td2a8ff_localTff7b72.Td2a8ffconnectionsTff7b72.Td2a8ffvaluesTb4b4b4(Tb4b4b4)Tb4b4b4:
Te6edf3connTff7b72.Td2a8ffcloseTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ff_localTff7b72.Td2a8ffconnections Tff7b72= Tb4b4b4{Tb4b4b4}
Tff7b72selfTff7b72.Td2a8ff_metadata_cache Tff7b72= Tff7b72None
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────